package com.epam.concurrency.threadqueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Random;

/**
 * My implementation of thread queue
 * threads in the queue are launched one by one
 * when one thread is running, all other threads are waiting
 */
public class ThreadQueue {

    private static Random rand = new Random();
    private static final int MAXSLEEPTIME = 1000;
    private static final int NUMWORKERS = 5;
    private static final Logger LOGGER = LoggerFactory.getLogger(ThreadQueue.class);

    private ThreadQueue() {
    }

    public static void main(String[] args) {
        Thread scheduler = Thread.currentThread();
        runThreadQueue(NUMWORKERS, scheduler);
    }

    /**
     * Run every worker in given array one by one
     * @param size number of threads in the queue
     * @param scheduler inspector to arrange what worker should work in the moment
     */
    public static void runThreadQueue(int size, Thread scheduler) {

        Worker[] workers = createQueue(size, scheduler);

        while(true) {
            for(Worker worker : workers) {
                //give the turn to the worker
                synchronized (worker) {
                    worker.activate();
                    worker.notify();
                }

                //wait for the thread to finish the job
                synchronized (scheduler) {
                    if(worker.isActive()) {
                        try {
                            scheduler.wait();
                        } catch (InterruptedException e) {
                            LOGGER.error(e.getMessage());
                        }
                    }
                }
            }
        }
    }

    /**
     * Create a queue of waiting workers
     * @param size size of the wanted queue
     * @return queue with workers waiting for job
     */
    private static Worker[] createQueue(int size, Thread scheduler) {
        Worker[] queue = new Worker[size];
        for(int i = 0; i < size; i++) {
            queue[i] = new Worker(i+1, scheduler);
            queue[i].start();
        }
        return queue;
    }

    /**
     * Standard worker of the thread queue
     */
    private static class Worker extends Thread {

        private final int id;
        private boolean active = false;
        private final Thread scheduler;

        public Worker(int id, Thread thread) {
            this.id = id;
            scheduler = thread;
        }

        /**
         * Give the turn to the current thread
         */
        public void activate() {
            active = true;
        }

        public boolean isActive() {
            return this.active;
        }

        @Override
        public void run() {
            while (!isInterrupted()) {
                try {
                    waitForTurn();
                } catch (InterruptedException e) {
                    LOGGER.error(e.getMessage());
                }

                LOGGER.info("Hello from Thread #" + this.id);

                //sleep for random amount of time to slow down thread
                try {
                    sleep(rand.nextInt(MAXSLEEPTIME));
                } catch (InterruptedException e) {
                    LOGGER.error(e.getMessage());
                }
                //deactivate current thread
                active = false;

                //send a message to the scheduler when the work is done
                synchronized (scheduler) {
                    scheduler.notify();
                }
            }
        }

        /**
         * Wait for activation of current worker
         * @throws InterruptedException
         */
        private synchronized void waitForTurn() throws InterruptedException {
            while (!active) {
                wait();
            }
        }
    }
}
